#ifndef     __ADC_H
#define     __ADC_H

#include "./compat.h"

namespace device::adc {

#define ADC(BASE)   reinterpret_cast<ADC_TypeDef *>(BASE)

template <uint32_t BASE>
inline void conver_init() {
    RCC->CFGR |= 0x03 << 14;
    ADC(BASE)->CR2 = 0x00;
    ADC(BASE)->CR1 = ADC_CR1_SCAN;
    ADC(BASE)->CR2 = ADC_CR2_CONT +
        (0x07 << 17) +
        ADC_CR2_DMA +
        ADC_CR2_EXTTRIG
        ;
}

template <uint32_t BASE>
inline void start_adj() {
    ADC(BASE)->CR2 |= ADC_CR2_ADON;

    ADC(BASE)->CR2 |= ADC_CR2_RSTCAL;
    while ((ADC(BASE)->CR2 & ADC_CR2_RSTCAL) != 0);

    ADC(BASE)->CR2 |= ADC_CR2_CAL;
    while ((ADC(BASE)->CR2 & ADC_CR2_CAL) != 0);
}

template <uint32_t BASE, uint8_t COUNT>
inline void config_cover_count() {
    static_assert((COUNT >= 1) && (COUNT <= 16), "COUNT FAIL");
    constexpr uint32_t MASK = 0x0F << 20;
    ADC(BASE)->SQR1 = (ADC(BASE)->SQR1 & (~MASK)) | ((COUNT - 1) << 20);
}

/**
 * @brief 通道CHAN 第ORDER次转换
 * 
 * @tparam BASE 
 * @tparam ORDER 
 * @tparam CHAN 
 */
template <uint32_t BASE, uint8_t ORDER, uint8_t CHAN>
inline void config_conver_order() {

    static_assert((ORDER >= 1) && (ORDER <= 16), "ORDER fail");
    static_assert((CHAN >= 0) && (CHAN <= 17), "chan fail");

    if constexpr ((ORDER >= 1) && (ORDER <= 6)) {
        constexpr uint32_t OFFSET = (ORDER - 1) * 5;
        constexpr uint32_t MASK = 0x1F << OFFSET;
        ADC(BASE)->SQR3 = (ADC(BASE)->SQR3 & (~MASK)) | (CHAN << OFFSET);
    } else if constexpr ((ORDER >= 7) && (ORDER <= 12)) {
        constexpr uint32_t OFFSET = (ORDER - 7) * 5;
        constexpr uint32_t MASK = 0x1F << OFFSET;
        ADC(BASE)->SQR2 = (ADC(BASE)->SQR2 & (~MASK)) | (CHAN << OFFSET);
    } else if constexpr ((ORDER >= 13) && (ORDER <= 16)) {
        constexpr uint32_t OFFSET = (ORDER - 13) * 5;
        constexpr uint32_t MASK = 0x1F << OFFSET;
        ADC(BASE)->SQR1 = (ADC(BASE)->SQR1 & (~MASK)) | (CHAN << OFFSET);
    }

    if constexpr ((CHAN >= 0) && (CHAN <= 9)) {
        constexpr uint32_t OFFSET = CHAN * 3;
        constexpr uint32_t VALUE = 0x07 << OFFSET;
        ADC(BASE)->SMPR2 = ADC(BASE)->SMPR2 | VALUE;
    } else if constexpr ((CHAN >= 10) && (CHAN <= 17)) {
        constexpr uint32_t OFFSET = (CHAN - 10) * 3;
        constexpr uint32_t VALUE = 0x07 << OFFSET;
        ADC(BASE)->SMPR1 = ADC(BASE)->SMPR1 | VALUE;
    }
}

template <uint32_t BASE, uint8_t ORDER, uint8_t CHAN, uint8_t... CHANS>
inline void config_conver_order_list() {
    config_conver_order<BASE, ORDER, CHAN>();
    constexpr uint32_t NEXT_COUNT = sizeof...(CHANS);
    if constexpr (NEXT_COUNT != 0) {
        config_conver_order_list<BASE, ORDER + 1, CHANS...>();
    }
}

template <uint32_t BASE, typename DMA, typename DMA_CHAN, uint8_t... CHANS>
inline void init(uint16_t *buf) {
    conver_init<BASE>();
    constexpr uint32_t COUNT = sizeof...(CHANS);
    static_assert(COUNT != 0, "NOT CHANS");
    
    config_cover_count<BASE, COUNT>();
    config_conver_order_list<BASE, 1, CHANS...>();
    start_adj<BASE>();

    DMA::clear_flags();
    DMA_CHAN::init_with_adc(
        reinterpret_cast<uint32_t>(buf), 
        reinterpret_cast<uint32_t>(&ADC(BASE)->DR),
        COUNT
    );
    ADC(BASE)->CR2 |= ADC_CR2_SWSTART;
}

}

#endif
